package com.threeti.mecool.web.assembler;

import com.threeti.mecool.core.application.acl.UserService;
import com.threeti.mecool.core.domain.model.Communication;
import com.threeti.mecool.core.domain.model.Memo;
import com.threeti.mecool.core.domain.model.acl.User;
import com.threeti.mecool.core.domain.model.plan.pojo.Job;
import com.threeti.mecool.web.form.MemoForm;
import com.threeti.mecool.web.util.DozerMapperSingleton;
import org.apache.commons.lang.StringUtils;
import org.dozer.Mapper;
import org.joda.time.DateTime;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;

import javax.persistence.criteria.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import static com.threeti.mecool.core.infrastructure.persistence.MemoCriteria.eqUserId;
import static com.threeti.mecool.core.infrastructure.persistence.MemoCriteria.likeChannelName;
import static org.springframework.data.jpa.domain.Specifications.where;

public abstract class PlanAssembler {
    public static Memo toDom(MemoForm memoForm, UserService userService) {
        Mapper transfer = DozerMapperSingleton.getInstance();
        Memo destDom = new Memo();
        transfer.map(memoForm, destDom);

        destDom.setUser(userService.getUserFromSession());
        return destDom;
    }

    public static Memo toDom(MemoForm memoForm) {
        Mapper transfer = DozerMapperSingleton.getInstance();
        Memo destDom = new Memo();
        transfer.map(memoForm, destDom);
        return destDom;
    }

    public static Specification<Memo> toCriteria(User user, MemoForm memoForm) {
        Specifications<Memo> spec = where(eqUserId(user.getId()));
        if (!memoForm.hasAnyConditionInputed()) {
            return spec;
        }
        if (StringUtils.isNotBlank(memoForm.getChannelName())) {
            spec = spec.and(likeChannelName(memoForm.getChannelName()));
        }
        return spec;
    }

    public static Specification<Job> toActiveCriteria(Map<String, String> paramMap) {
        Specifications<Job> spec = where(new Specification<Job>() {
            @Override
            public Predicate toPredicate(Root<Job> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.or(root.<Integer>get("removed").isNull(), cb.equal(root.<Integer>get("removed"), 0));
            }
        });
        if (StringUtils.isNotBlank(paramMap.get("jobName"))) {
            final String jobName = paramMap.get("jobName");
            spec = spec.and(new Specification<Job>() {
                public Predicate toPredicate(Root<Job> root,
                                             CriteriaQuery<?> query,
                                             CriteriaBuilder cb) {
                    Path<String> path = root.get("name");
                    return cb.like(path, "%" + jobName + "%");
                }
            });
        }
        return spec;
    }

    public static Specification<Job> toCriteria(Map<String, String> paramMap) {
        Specifications<Job> spec = null;
        if (StringUtils.isNotBlank(paramMap.get("jobName"))) {
            final String jobName = paramMap.get("jobName");

            spec = Specifications.where(new Specification<Job>() {
                public Predicate toPredicate(Root<Job> root,
                                             CriteriaQuery<?> query,
                                             CriteriaBuilder cb) {
                    Path<String> path = root.get("name");
                    return cb.like(path, "%" + jobName + "%");
                }
            });
        }
        return spec;
    }

    public static Specification<Communication> buildSubordinateCommunicationSpecs(final Map<String, Object> modelMap) {
        Specifications<Communication> specs = Specifications.where(new Specification<Communication>() {
            @Override
            public Predicate toPredicate(Root<Communication> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return root.get("user").get("loginName").in(modelMap.get("userLoginNames"));
            }
        });
        if (StringUtils.isNotBlank((String) modelMap.get("projectName"))) {
            specs = specs.and(new Specification<Communication>() {
                @Override
                public Predicate toPredicate(Root<Communication> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    return cb.like(root.get("project").<String>get("name"), "%" + modelMap.get("projectName").toString().trim() + "%");
                }
            });
        }
        if (StringUtils.isNotBlank((String) modelMap.get("channelName"))) {
            specs = specs.and(new Specification<Communication>() {
                @Override
                public Predicate toPredicate(Root<Communication> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    return cb.like(root.get("projectStore").get("channel").get("channel").<String>get("name"), "%" + modelMap.get("channelName").toString().trim() + "%");
                }
            });
        }
        if (StringUtils.isNotBlank((String) modelMap.get("recordDateFrom"))) {
            specs = specs.and(new Specification<Communication>() {
                @Override
                public Predicate toPredicate(Root<Communication> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    try {
                        Date date = new SimpleDateFormat("yyyy-MM-dd").parse(modelMap.get("recordDateFrom").toString());
                        return cb.greaterThanOrEqualTo(root.<Date>get("recordDate"), date);
                    } catch (ParseException e) {
                        throw new RuntimeException("invalid date format");
                    }
                }
            });
        }
        if (StringUtils.isNotBlank((String) modelMap.get("recordDateTo"))) {
            specs = specs.and(new Specification<Communication>() {
                @Override
                public Predicate toPredicate(Root<Communication> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    try {
                        Date date = new SimpleDateFormat("yyyy-MM-dd").parse(modelMap.get("recordDateTo").toString());
                        DateTime day = new DateTime(date).plusDays(1);
                        return cb.lessThan(root.<Date>get("recordDate"), day.toDate());
                    } catch (ParseException e) {
                        throw new RuntimeException("invalid date format");
                    }
                }
            });
        }
        if (StringUtils.isNotBlank((String) modelMap.get("userLoginName"))) {
            specs = specs.and(new Specification<Communication>() {
                @Override
                public Predicate toPredicate(Root<Communication> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    return cb.equal(root.get("user").get("loginName"), modelMap.get("userLoginName").toString());
                }
            });
        }
        return specs;
    }
}
